﻿# -*- coding: utf_8 -*-
from Bone import Bone
import xml.dom.minidom as minixml

def cmp_list(l1, l2):
    assert(len(l1) == len(l2))
    for i in xrange(0, len(l1)):
        v1 = l1[i]
        v2 = l2[i]
        assert(type(v1) == float)
        assert(type(v2) == float)
        if abs(v1 - v2) > 1e-5:
            return False
            
    return True

class CSkeletonExport:
    def __init__(self):
        self._boneList = []
        self._frameCount = 1
        self._relative = True
        self._outputFile = ''
        self._aniName = ''
        
    def DoExport(self, xmlDoc, aniName, outputFile, meshBone, meshBoneList):
        print 'Export skeleton animation %s' % aniName
        for node in xmlDoc.childNodes:
            if node.nodeName == 'IGame':
                igame = node
                for node in igame.childNodes:
                    if not meshBone and node.nodeName == 'Node' and (node.getAttribute('NodeType') == 'Bone' or node.getAttribute('NodeType') == 'Helper'):
                        bone = Bone()
                        self._boneList.append(bone)
                        bone.Parse(node, self._boneList, meshBone, meshBoneList)
                    elif meshBone and node.nodeName == 'Node' and not (node.getAttribute('Name') in meshBoneList):
                        bone = Bone()
                        self._boneList.append(bone)
                        bone.Parse(node, self._boneList, meshBone, meshBoneList)
                    elif node.nodeName == 'SceneInfo':
                        if node.getAttribute('ControllerRelative') == 'true':
                            self._relative = True
                        else:
                            self._relative = False                        
        
        self.__CombineAnimation()
        self.__CalculateFrameCount()
        
        self._outputFile = outputFile
        self._aniName = aniName
        
        print 'Frame count: [%d] Bone count: [%d]' % (self._frameCount, len(self._boneList))
                        
    def __CombineAnimation(self):
        for bone in self._boneList:
            ## combine same translation
            clear = True
            for translation in bone._aniTranslation:
                if not cmp_list(translation, bone._transformation._position):
                    clear = False
                    break
            
            if clear:
                bone._aniTranslation = []
            
            ## combine same rotation    
            clear = True
            for rotation in bone._aniRotation:
                if not cmp_list(rotation, bone._transformation._rotation):
                    clear = False
                    break
                    
            if clear:
                bone._aniRotation = []
                
            ## combine same scale
            clear = True
            for scale in bone._aniScale:
                if not cmp_list(scale, bone._transformation._scale):
                    clear = False
                    break
                    
            if clear:
                bone._aniScale = []   
                
            ## combine same scale rotation
            clear = True
            for scale_rotation in bone._aniScaleRotation:
                if not cmp_list(scale_rotation, bone._transformation._scaleRotation):
                    clear = False
                    break
                    
            if clear:
                bone._aniScaleRotation = []   
                                
            ## make a translation animation at least
            if len(bone._aniTranslation) == 0:
                bone._aniTranslation.append(bone._transformation._position)
                
            ## make a rotation animation at least
            if len(bone._aniRotation) == 0:
                bone._aniRotation.append(bone._transformation._rotation)
            
            ## make a scale animation at least    
            if len(bone._aniScale) == 0:
                bone._aniScale.append(bone._transformation._scale) 
                
            ## make a scale rotation animation at least
            if len(bone._aniScaleRotation) == 0:
                bone._aniScaleRotation.append(bone._transformation._scaleRotation)
            
               
    def __CalculateFrameCount(self):
        for bone in self._boneList:         
            frameNum = len(bone._aniTranslation)
            if frameNum > self._frameCount:
                if self._frameCount == 1:
                    self._frameCount = frameNum
                else:
                    assert(frameNum == self._frameCount)
                        
            frameNum = len(bone._aniRotation)
            if frameNum > self._frameCount:
                if self._frameCount == 1:
                    self._frameCount = frameNum
                else:
                    assert(frameNum == self._frameCount)
                        
            frameNum = len(bone._aniScale)
            if frameNum > self._frameCount:
                if self._frameCount == 1:
                    self._frameCount = frameNum
                else:
                    assert(frameNum == self._frameCount)
                    
            frameNum = len(bone._aniScaleRotation)
            if frameNum > self._frameCount:
                if self._frameCount == 1:
                    self._frameCount = frameNum
                else:
                    assert(frameNum == self._frameCount)                    
            
    def WriteFile(self):
        try:
            impl = minixml.getDOMImplementation()
            newDoc = impl.createDocument(None, None, None)

            skeletonElem = newDoc.createElement('Skeleton')
            skeletonElem.setAttributeNode(newDoc.createAttribute('Name'))
            skeletonElem.setAttribute('Name', self._aniName)
            skeletonElem.setAttributeNode(newDoc.createAttribute('BoneNum'))
            skeletonElem.setAttribute('BoneNum', str(len(self._boneList)))
            skeletonElem.setAttributeNode(newDoc.createAttribute('AnimationFrame'))
            skeletonElem.setAttribute('AnimationFrame', str(self._frameCount))
            skeletonElem.setAttributeNode(newDoc.createAttribute('Relative'))
            skeletonElem.setAttribute('Relative', str(int(self._relative)))
            newDoc.childNodes.append(skeletonElem)
            
            for idxBone in xrange(0, len(self._boneList)):
                bone = self._boneList[idxBone]
                boneElem = newDoc.createElement('Bone')
                skeletonElem.childNodes.append(boneElem)
                
                boneElem.setAttributeNode(newDoc.createAttribute('name'))
                boneElem.setAttribute('name', bone._name)
                
                boneElem.setAttributeNode(newDoc.createAttribute('parentIdx'))
                if not bone._parentId:
                    boneElem.setAttribute('parentIdx', '-1')
                else:
                    for i in xrange(0, len(self._boneList)):
                        b = self._boneList[i]
                        if b._id == bone._parentId:
                            boneElem.setAttribute('parentIdx', str(i))

                ## Translation
                aniFrame = len(bone._aniTranslation)
                assert(aniFrame != 0)
                aniTransElem = newDoc.createElement('Translation')
                boneElem.childNodes.append(aniTransElem)
                for pos in bone._aniTranslation:
                    x, y, z = pos[0], pos[2], pos[1]    ## exchange y and z
                    frameElem = newDoc.createElement('Frame')
                    aniTransElem.childNodes.append(frameElem)
                    frameElem.setAttributeNode(newDoc.createAttribute('x'))
                    frameElem.setAttribute('x', str(x))
                    frameElem.setAttributeNode(newDoc.createAttribute('y'))
                    frameElem.setAttribute('y', str(y))
                    frameElem.setAttributeNode(newDoc.createAttribute('z'))
                    frameElem.setAttribute('z', str(z))
            
                ## Rotation
                rotFrame = len(bone._aniRotation)
                if rotFrame > 1:
                    if aniFrame == 1:
                        aniFrame = rotFrame
                    else:
                        assert(aniFrame == len(bone._aniRotation))

                aniRotElem = newDoc.createElement('Rotation')
                boneElem.childNodes.append(aniRotElem)
                for rot in bone._aniRotation:
                    x, y, z, w = rot[0], rot[2], rot[1], rot[3] ## exchange y and z
                    frameElem = newDoc.createElement('Frame')
                    aniRotElem.childNodes.append(frameElem)
                    frameElem.setAttributeNode(newDoc.createAttribute('x'))
                    frameElem.setAttribute('x', str(x))
                    frameElem.setAttributeNode(newDoc.createAttribute('y'))
                    frameElem.setAttribute('y', str(y))
                    frameElem.setAttributeNode(newDoc.createAttribute('z'))
                    frameElem.setAttribute('z', str(z))
                    frameElem.setAttributeNode(newDoc.createAttribute('w'))
                    frameElem.setAttribute('w', str(w))
                        
                ## Scale             
                scaleFrame = len(bone._aniScale)
                if scaleFrame > 1:       
                    if aniFrame == 1:
                        aniFrame = scaleFrame
                    else:
                        assert(aniFrame == len(bone._aniScale))
                                        
                aniScaleElem = newDoc.createElement('Scale')
                boneElem.childNodes.append(aniScaleElem)
                for scale in bone._aniScale:
                    x, y, z = scale[0], scale[2], scale[1]  ## exchange y and z
                    frameElem = newDoc.createElement('Frame')
                    aniScaleElem.childNodes.append(frameElem)
                    frameElem.setAttributeNode(newDoc.createAttribute('x'))
                    frameElem.setAttribute('x', str(x))
                    frameElem.setAttributeNode(newDoc.createAttribute('y'))
                    frameElem.setAttribute('y', str(y))
                    frameElem.setAttributeNode(newDoc.createAttribute('z'))
                    frameElem.setAttribute('z', str(z))            
            
                srFrame = len(bone._aniScaleRotation)
                if srFrame> 1:
                    if aniFrame == 1:
                        aniFrame = srFrame
                    else:
                        assert(aniFrame == len(bone._aniScaleRotation))
                    
                aniScaleRotationElem = newDoc.createElement('ScaleRotation')
                boneElem.childNodes.append(aniScaleRotationElem)
                for sr in bone._aniScaleRotation:
                    x, y, z, w = sr[0], sr[2], sr[1], sr[3]  ## exchange y and z
                    frameElem = newDoc.createElement('Frame')
                    aniScaleRotationElem.childNodes.append(frameElem)
                    frameElem.setAttributeNode(newDoc.createAttribute('x'))
                    frameElem.setAttribute('x', str(x))
                    frameElem.setAttributeNode(newDoc.createAttribute('y'))
                    frameElem.setAttribute('y', str(y))
                    frameElem.setAttributeNode(newDoc.createAttribute('z'))
                    frameElem.setAttribute('z', str(z))            
                    frameElem.setAttributeNode(newDoc.createAttribute('w'))
                    frameElem.setAttribute('w', str(w))
                    
                    

            newDoc.writexml(open(self._outputFile, 'w'), newl = '\r\n', addindent = '    ', encoding = 'utf-8')
            newDoc.unlink()
            
        except:
            SHOW_TRACE()